﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;

namespace CH.Study.ConsoleApp.Emit
{
    public class Photo
    {
        public virtual int ID { get; set; }
        public virtual string Title { get; set; }
        public virtual DateTime AddTime { get; set; }
    }


    public sealed class Entity<TEntity>
    {
        private static AssemblyBuilder assBuilder = null;
        private static ModuleBuilder modBuilder = null;
        private const string DYNAMIC_ENTITY_NAMESPACE = "CH.Easy.DbProvider.DynamicEntity";
        private static TypeBuilder typeBuilder = null;

        static Entity()
        {

            if (assBuilder == null)
            {
                //在当前应用域创建一个程序集
                AssemblyName assName = new AssemblyName();
                assName.Name = DYNAMIC_ENTITY_NAMESPACE;
                assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave);
            }
            if (modBuilder == null)
            {
                //在程序集中创建模块
                modBuilder = assBuilder.DefineDynamicModule(DYNAMIC_ENTITY_NAMESPACE);
            }

            if (typeBuilder == null)
            {
                //在模块中创建类型
                typeBuilder = modBuilder.DefineType(DYNAMIC_ENTITY_NAMESPACE + "." + typeof(TEntity).FullName, TypeAttributes.Public);
            }
            //设置类型的父类
            typeBuilder.SetParent(typeof(TEntity));



            FieldBuilder kvFieldBuilder = typeBuilder.DefineField("kv", typeof(Dictionary<string, object>), FieldAttributes.Public);

            //创建构造函数
            ConstructorBuilder consBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
            ILGenerator ctorIL = consBuilder.GetILGenerator();
            //加载索引为0 的参数 一般this对象 即当前类型实例
            ctorIL.Emit(OpCodes.Ldarg_0);
            //创建一个类型为Dictionary<string, object> 的实例 并放入堆栈中
            ctorIL.Emit(OpCodes.Newobj, typeof(Dictionary<string, object>).GetConstructor(Type.EmptyTypes));
            //将kvFieldBuilder地址引用的值 替换为堆栈中的值
            ctorIL.Emit(OpCodes.Stfld, kvFieldBuilder);
            ctorIL.Emit(OpCodes.Ret);




            //创建方法
            MethodBuilder changeValueMethodBuilder = typeBuilder.DefineMethod("ChangeValue", MethodAttributes.Public, null, new Type[] { typeof(string), typeof(object) });
            ILGenerator cvmil = changeValueMethodBuilder.GetILGenerator();


            //定义分界点
            //假分界点
            Label lbFalse = cvmil.DefineLabel();
            //返回分界点
            Label lbRe = cvmil.DefineLabel();

            //加载参数 this 到堆栈
            cvmil.Emit(OpCodes.Ldarg_0);
            //查找kvFieldBuilder 到堆栈
            cvmil.Emit(OpCodes.Ldfld, kvFieldBuilder);
            //加载参数1 即key 到堆栈
            cvmil.Emit(OpCodes.Ldarg_1);
            //调用get_Keys 返回值的方法Contains 并把结果保存到堆栈
            cvmil.Emit(OpCodes.Callvirt, typeof(Dictionary<string, object>).GetMethod("ContainsKey"));
            //加载整形1(即为真) 到堆栈
            cvmil.Emit(OpCodes.Ldc_I4_1);
            //把刚才的结果与整形1 进行比较 并把结果保存到堆栈
            cvmil.Emit(OpCodes.Ceq);
            cvmil.Emit(OpCodes.Brfalse, lbFalse);
            //加载参数 this 到堆栈
            cvmil.Emit(OpCodes.Ldarg_0);
            //查找kvFieldBuilder 到堆栈
            cvmil.Emit(OpCodes.Ldfld, kvFieldBuilder);
            //加载参数1 即key 到堆栈
            cvmil.Emit(OpCodes.Ldarg_1);
            //加载参数1 即value 到堆栈
            cvmil.Emit(OpCodes.Ldarg_2);
            //调用kvFieldBuilder 方法set_Item
            cvmil.Emit(OpCodes.Callvirt, typeof(Dictionary<string, object>).GetMethod("set_Item"));
            //执行完以后跳转到返回分界点
            cvmil.Emit(OpCodes.Br, lbRe);


            cvmil.MarkLabel(lbFalse);
            //加载参数 this 到堆栈
            cvmil.Emit(OpCodes.Ldarg_0);
            //查找kvFieldBuilder 到堆栈
            cvmil.Emit(OpCodes.Ldfld, kvFieldBuilder);
            //加载参数1 即key 到堆栈
            cvmil.Emit(OpCodes.Ldarg_1);
            //加载参数1 即value 到堆栈
            cvmil.Emit(OpCodes.Ldarg_2);
            //调用kvFieldBuilder 方法Add
            cvmil.Emit(OpCodes.Callvirt, typeof(Dictionary<string, object>).GetMethod("Add"));
            cvmil.Emit(OpCodes.Br, lbRe);
            cvmil.MarkLabel(lbRe);
            cvmil.Emit(OpCodes.Ret);



















            var pis = typeof(TEntity).GetProperties();

            foreach (var pi in pis)
            {


                //定义私有字段_a
                FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + pi.Name.ToLower(), pi.PropertyType, FieldAttributes.Private);
                //定义公有属性A
                PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.None, pi.PropertyType, null);



                //定义属性A的get方法 并标记为 重写方法
                MethodBuilder getPropertyBuilder = typeBuilder.DefineMethod("get_" + pi.Name, pi.GetGetMethod().Attributes, pi.PropertyType, Type.EmptyTypes);
                //设置属性A的get方法 重写pi.GetGetMethod()的方法
                typeBuilder.DefineMethodOverride(getPropertyBuilder, pi.GetGetMethod());
                //生成属性A的get方法的IL代码，即返回私有字段_a
                ILGenerator getIL = getPropertyBuilder.GetILGenerator();
                getIL.Emit(OpCodes.Ldarg_0);
                getIL.Emit(OpCodes.Ldfld, fieldBuilder);
                getIL.Emit(OpCodes.Ret);



                //定义属性A的set方法 并标记为 重写方法
                MethodBuilder setPropertyBuilder = typeBuilder.DefineMethod("set_" + pi.Name, pi.GetSetMethod().Attributes, null, new Type[] { pi.PropertyType });
                //设置属性A的set方法 重写pi.GetGetMethod()的方法
                typeBuilder.DefineMethodOverride(setPropertyBuilder, pi.GetSetMethod());
                //生成属性A的set方法的IL代码，即设置私有字段_a值为传入的参数1的值
                ILGenerator setIL = setPropertyBuilder.GetILGenerator();
                setIL.Emit(OpCodes.Ldarg_0);
                setIL.Emit(OpCodes.Ldarg_1);
                //将fieldABuilder地址引用的值 替换为堆栈中的值
                setIL.Emit(OpCodes.Stfld, fieldBuilder);


                setIL.Emit(OpCodes.Ldarg_0);
                setIL.Emit(OpCodes.Ldstr, pi.Name);
                setIL.Emit(OpCodes.Ldarg_1);
                if (pi.PropertyType.IsValueType)
                {
                    setIL.Emit(OpCodes.Box, pi.PropertyType);
                }
                setIL.Emit(OpCodes.Call, changeValueMethodBuilder);
                setIL.Emit(OpCodes.Ret);



                //设置属性A的get和set方法
                propertyBuilder.SetGetMethod(getPropertyBuilder);
                propertyBuilder.SetSetMethod(setPropertyBuilder);

            }






        }


        public static TEntity New()
        {

            return (TEntity)Activator.CreateInstance(typeBuilder.CreateType());
        }
    }


    public class EmitTest : ITest
    {
        Dictionary<string, object> dic;
        public void Start()
        {
            dic = new Dictionary<string, object>();

            Photo p = Entity<Photo>.New();
            p.ID = 100;
            p.Title = "fdsa";
            p.AddTime = DateTime.Now;
            p.ID = 200;
            p.ID = 500;
            Photo p2 = Entity<Photo>.New();
            p2.ID = 300;
            p2.Title = "sss";
            p2.AddTime = DateTime.Now;
            p2.ID = 400;
            Console.WriteLine(p.ID);
        }

        public void ChangeValue(string key, object value)
        {

            if (dic.ContainsKey(key))
            {
                dic[key] = value;
            }
            else
            {
                dic.Add(key, value);
            }
        }

    }
}
